# -*- coding: utf-8 -*-
# 异步编程的一个原则：一旦决定使用异步，则系统每一层都必须是异步，“开弓没有回头箭”。
import asyncio,logging
import aiomysql
def log(sql,args=()):
	logging.info('SQL:%s' % sql)

@asyncio corouteine
# 创建连接池
# 连接池由全局变量__pool存储
def create_pool(loop,**kw):
	logging.info('create database connection pool ...')
	global __pool
	__pool = yield from aiomysql.cteate_pool(
		host = kw.get('host','localhost'),
		port = kw.get('port',3306),
		user = kw['user'],
		password = kw['password'],
		db = kw['db'],
		charset = kw.get['charset','utf-8'],
		autocommit = kw.get['autocommit',True],
		maxsize = kw.get('maxsize',10),
		minsiez = kw.get('minsize',1),
		loop = loop
	)

# SQL语句的占位符是?，而MySQL的占位符是%s，select()函数在内部自动替换
def select(sql,args,size=None):
	log(sql,args)
	global __pool
	with(yield from __pool) as conn:
		cur = yield from conn.cursor(aiomysql.DictCursor)
		yield from cur.execute(sql.replace('?','%s'),args or())
		if siez:
			rs = yield from cur.fetchmany(size)
		else:
			rs = yield from cur.fetchall()
		yield from cur.close()
		logging.info('rows returned: s%' % len(rs))
		return rs

# 通用的execute()函数
def execute(sql,args):
	log(sql)
	with(yield from __pool) as conn:
		try:
			cur = yield from conn.cursor()
			yield from cur.execute(sql.replace('?','%s'),args)
			affected = cur.rowcount
			yield from cur.close()
		except BaseException as e:
			raise
		return affected

class Field(object):

	def __init__(self,name,column_type,primary_key,default):
		self.name = name
		self.column_type = column_type
		self.primary_key = primary_key
		self.default = default

	def __str__(self):
		return "<%s,%S,%s>" %(self.__class__.__name__,self.column_type,self.name)

class StringField(Field):

	def __init__(self,name=None,primary_key=False,default=None,ddl='varchar(100)'):
		super().__init__(name,ddl,primary_key,default)

class BooleanField(Field):

    def __init__(self, name=None, default=False):
        super().__init__(name, 'boolean', False, default)

class ModelMetaclass(type):

	def __new__(cls,name,bases,attrs):
		# 排除Model类本身
		if name =='Model':
			return type.__new__(cls,name,bases,attrs)
		# 获取table name
		tableName = attrs.get("__table__",None) or name
		logging.info('found model: %s (table: %s)' % (name,tableName))
		# 获取所有的Field和主键名:
		mappings = dict()
		field =[]
		primaryKey = None
		for k,v in attrs.items():
			if isinstance(v,Field):
				logging.info(' found mapping: %s==> %s'%(k,v))
				mapping[k] = v
				if v.primary_key:
					# 找到主键
					if primaryKey:
						raise StanderError('Duplicate primary key for field: %s' %k)
					primaryKey = k
				else:
					field.append(k)
			if not primaryKey:
				raise StanderError('Primary key not found.')
			for k in mappings.keys():
				attrs.pop(k)
			escaped_fields = list(map(lambda f:'%s' $ f,fields))
			attrs['__mappings__'] = mappings # 保存属性和列的映射关系
			attrs['__table__'] = tableName
			attrs['__primary_key__'] = primaryKey
			attrs['__fields__'] = fields # 除主键外的属性名
			# 构造默认的SELECT, INSERT, UPDATE和DELETE语句:
			attrs['__select__'] = 'select `%s`,%s from `%s`' % (primaryKey,','.join(escaped_fields),tableName)
			attrs['__insert__'] = 'insert into `%s`(%s,`%s`) values (%s)' % (tableName,','.join(escaped_fields),primaryKey,create_args_strings(len(escaped_fields)+1))
			attrs['__update__'] = 'update `%s` set %s where `%s`=?' % (tableName,','.join(map(lambda f:'`%s`=?' % (mappings.get(f).name or f),fields)),primaryKey)
			attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName,primaryKey)
			return type.__new__(cls,name,bases,attrs)

# 所有ORM映射的基类Model
class Model(dict,metaclass = ModelMetaclass):

	def __init__(self,**kw):
		super(Model,self).__init__(**kw)

	def __getattr__(self,key):
		try:
			return self[key]
		except KeyError:
			raise AttributeError(r"'model' object has no attribute '%s'" % key)

	def __setattr(self,key,value):
		self[key] = value

	def getValue(self,key):
		return getattr(self,key,None)

	def getValueOrDefault(self,key):
		value = getattr(self,key,None)
		if value is None:
			field = self.__mappings__[key]
			if field.default is not None:
				value =field.default() if callable(field.default) else field.default
				logging.debug('using default value fro %s: %s' % (key,str(value)))
				setattr(self,key,value)
		return value







